#include "p_parser.h"

#include "p_file_utils.h"
#include "p_parser_expr.h"

const p_token_struct p_parser_struct::EOF_TOKEN = token_struct_create(TOK_EOF);

// -----------------------------------------------------------------------------
// ---------------------------  PROTOTYPES   -----------------------------------
// -----------------------------------------------------------------------------

void parser_parse_program(p_parser_struct_ptr parser);
void parser_parse_type_expression(p_parser_struct_ptr parser,
                                  const bool &check_semicolon);
void parser_parse_statement(p_parser_struct_ptr parser);
void parser_parse_function_definition(p_parser_struct_ptr parser);

// -----------------------------------------------------------------------------

p_parser_struct parser_struct_create()
{
   p_parser_struct result;
   result.token_list = 0;
   return result;
}

void parser_struct_init(p_parser_struct_ptr parser)
{
   if (parser) {
      parser->token_list = 0;
      parser->token_index = 0;
      parser->lvalue_expression = false;
   }
}

void parser_struct_clean_up(p_parser_struct_ptr parser)
{
   if (parser) {
      if (parser->token_list) {
         delete parser->token_list;
         parser->token_list = 0;
      }
   }
}

p_parser_struct_ptr parser_struct_new()
{
   p_parser_struct_ptr result = new p_parser_struct();

   parser_struct_init(result);

   return result;
}

void parser_struct_destroy(p_parser_struct_ptr parser)
{
   if (parser) {
      parser_struct_clean_up(parser);

      delete parser;
   }
}

void parser_struct_parse(p_parser_struct_ptr parser)
{
   // Well just for now we have to programs only
   parser_parse_program(parser);
}

p_parser_struct_ptr parser_parse(const std::string &file_name_or_content,
                                 const bool &is_file_name)
{
   if (is_file_name) {
      return parser_parse_file(file_name_or_content);
   } else {
      return parser_parse(file_name_or_content);
   }
}

p_parser_struct_ptr parser_parse(const std::string &program)
{
   p_parser_struct_ptr parser = parser_struct_new();

   // first creating a lexer and then instantiating the tokenization process..
   parser->token_list = tokenize(program);

   // now invoking the parsing process...
   parser_struct_parse(parser);

   return parser;
}

p_parser_struct_ptr parser_parse_file(const std::string &file_name)
{
   // reading the file content...
   if (file_exists(file_name)) {
      std::string file_content = file_read(file_name);
      return parser_parse(file_content);
   } else {
      throw error_struct_create("File '" + file_name + "' not found!");
   }
}

// -----------------------------------------------------------------------------

const p_token_struct& next_token(p_parser_struct_ptr parser)
{
   if (parser && parser->token_list && (parser->token_index
            < parser->token_list->size())) {
      return (*parser->token_list)[parser->token_index++];
   }
   return p_parser_struct::EOF_TOKEN;
}

const p_token_struct& test_token(p_parser_struct_ptr parser)
{
   if (parser && parser->token_list && (parser->token_index
            < parser->token_list->size())) {
      return (*parser->token_list)[parser->token_index];
   }
   return p_parser_struct::EOF_TOKEN;
}

// -----------------------------------------------------------------------------

/**
 * This function parses a line within a record type definition. A record
 * is a set of lines which can be of many different types.
 *
 * @param parser The parser object which contains every setting.
 */
void parser_parse_type_record_line(p_parser_struct_ptr parser)
{
   // a record line starts with an identifier...
   const p_token_struct& curr_tok = next_token(parser);
   if (curr_tok.type != TOK_IDENT) throw error_struct_create(
                                                             "Identifier expected as the first token in a record definition");
   if (next_token(parser).type != TOK_DPOINT) throw error_struct_create(
                                                                        "':' expected after an identifier in a record definition");
   parser_parse_type_expression(parser, true);
}

/**
 * This function parses a record type definition. A record contains several lines
 * of {@link parser_parse_type_record_line()}.
 *
 * @param parser The parser object which contains every setting.
 */
void parser_parse_type_record(p_parser_struct_ptr parser)
{
   while (test_token(parser).type != TOK_END) {
      parser_parse_type_record_line(parser);
   }

   if (next_token(parser).type != TOK_END) {
      throw error_struct_create(
                                "'end' expected at the end of a record definition");
   }
}

/**
 * This function parses a primitive type containing the following types:
 *    [signed | unsigned|] [byte|word|dword|tbyte]
 *
 * @param parser This is the parser object which contains every setting.
 * @param enforce This tells the function whether primitive types are
 *    mandatory or not.
 */
bool parser_parse_primitive_type(p_parser_struct_ptr parser,
                                 const bool &enforce)
{
   const p_token_struct& curr_tok = test_token(parser);
   switch (curr_tok.type)
   {
   case TOK_BYTE:
   case TOK_WORD:
   case TOK_DWORD:
   case TOK_TBYTE:
      // consuming the token...
      next_token(parser);
      return true;
   default:
      if (enforce) {
         throw error_struct_create("A primitive type expected");
      }
   }
   return false;
}

/**
 * This is the main type expression parsing function.
 *
 * @param parser This is the parser object which contains every setting.
 * @param check_semicolon Tells the function that at the end of an expression
 *    a semicolon is expected.
 */
void parser_parse_type_expression(p_parser_struct_ptr parser,
                                  const bool &check_semicolon)
{
   if (!parser_parse_primitive_type(parser, false)) {
      // consuming the token...
      const p_token_struct& curr_tok = next_token(parser);
      switch (curr_tok.type)
      {
      case TOK_SIGNED:
      case TOK_UNSIGNED:
         parser_parse_primitive_type(parser, true);
         break;
      case TOK_ADDR: // pointer types...
         parser_parse_type_expression(parser, false);
         break;
      case TOK_RECORD:
         parser_parse_type_record(parser);
         break;
      default:
         throw error_struct_create("Type expression expected");
      }
   }

   if (check_semicolon) {
      if (next_token(parser).type != TOK_SEMICOLON) throw error_struct_create(
                                                                              "';' expected at the end of a statement");
   }
}

// -----------------------------------------------------------------------------

/**
 * This is the main parser function for var statements.
 *
 * @param parser This is the parser object which contains every setting.
 */
void parser_parse_var_statement(p_parser_struct_ptr parser)
{
   // the first token has to be VAR...
   if (next_token(parser).type != TOK_VAR) throw error_struct_create(
                                                                     "'var' expected for a variable definition");

   // reading the name of the variable...
   if (test_token(parser).type != TOK_IDENT) throw error_struct_create(
                                                                       "Identifier expected for a variable name");
   std::string name = next_token(parser).ident;

   // only one identifier is allowed as var name
   if (next_token(parser).type != TOK_DPOINT) throw error_struct_create(
                                                                        "':' expected after an identifier");
   // next thing is the type expression...
   parser_parse_type_expression(parser, false);
}

/**
 * This is the function which parses a precondition.
 *
 * @param parser This is the object which contains every setting.
 */
void parser_parse_statement_precondition(p_parser_struct_ptr parser)
{
   if (next_token(parser).type != TOK_POINT) throw error_struct_create(
                                                                       "'.' expected for a precondition");
   // the first token is an identifier which follows immediately the '.' ...
   // for now we have to consume the token...
   const p_token_struct& curr_tok = next_token(parser);

   switch (curr_tok.type)
   {
   case TOK_READ_ONLY:
   case TOK_NAKED:
      break;
   case TOK_DLLIMPORT:
   {
      const p_token_struct& temp_tok = next_token(parser);
      if (temp_tok.type != TOK_RBRO) {
         throw error_struct_create(
                                   "'(' expected, but '" + temp_tok.ident
                                            + "' found");
      }

      // the next token has to be a string token, which contains the name of the
      // desired dynamic link library
      const p_token_struct& name_tok = next_token(parser);
      if (name_tok.type != TOK_STRING) {
         throw error_struct_create(
                                   "String token expected for dynamic link library file name");
      }

      const p_token_struct& temp_tok2 = next_token(parser);
      if (temp_tok2.type != TOK_RBRC) {
         throw error_struct_create(
                                   "')' expected, but '" + temp_tok2.ident
                                            + "' found");
      }

   }
      break;
   case TOK_PRECONDITION:
   case TOK_EXPRESSION:
   {
      const p_token_struct& temp_tok = next_token(parser);
      if (temp_tok.type != TOK_RBRO) {
         throw error_struct_create(
                                   "'(' expected, but '" + temp_tok.ident
                                            + "' found");
      }
      parser_parse_value_expression(parser);
      const p_token_struct& temp_tok2 = next_token(parser);
      if (temp_tok2.type != TOK_RBRC) {
         throw error_struct_create(

         "')' expected, but '" + temp_tok2.ident + "' found");
      }
   }
      break;
   default:
      throw error_struct_create(
                                "Unknown precondition '" + curr_tok.ident
                                         + "' found");
   }
}

/**
 * This is the function which parses a postcondition.
 *
 * @param parser This is the object which contains every setting.
 */
void parser_parse_statement_postcondition(p_parser_struct_ptr parser)
{
   if (next_token(parser).type != TOK_POINT) throw error_struct_create(
                                                                       "'.' expected for a precondition");
   // the first token is an identifier which follows immediately the '.' ...
   // for now we have to consume the token...
   const p_token_struct& curr_tok = next_token(parser);

   switch (curr_tok.type)
   {
   case TOK_POSTCONDITION:
   case TOK_EXPRESSION:
   {
      const p_token_struct& temp_tok = next_token(parser);
      if (temp_tok.type != TOK_RBRO) {
         throw error_struct_create(
                                   "'(' expected, but '" + temp_tok.ident
                                            + "' found");
      }
      parser_parse_value_expression(parser);
      const p_token_struct& temp_tok2 = next_token(parser);
      if (temp_tok2.type != TOK_RBRC) {
         throw error_struct_create(

         "')' expected, but '" + temp_tok2.ident + "' found");
      }
   }
      break;
   default:
      throw error_struct_create(
                                "Unknown postcondition '" + curr_tok.ident
                                         + "' found");
   }
}

/**
 * This function parses every preconditions.
 *
 * @param parser This is the object which contains every setting.
 */
void parser_parse_statement_preconditions(p_parser_struct_ptr parser)
{
   while (test_token(parser).type == TOK_POINT) {
      parser_parse_statement_precondition(parser);
   }
}

/**
 * This function parses every postconditions.
 *
 * @param parser This is the object which contains every setting.
 */
void parser_parse_statement_postconditions(p_parser_struct_ptr parser)
{
   while (test_token(parser).type == TOK_POINT) {
      parser_parse_statement_postcondition(parser);
   }
}

void parser_parse_statement_block(p_parser_struct_ptr parser,
                                  bool skip_first_ident,
                                  const std::string &block_name)
{
   // the first token is the name of the array of the blocks...

   std::string block_identifier = block_name;

   if (!skip_first_ident) {
      const p_token_struct& name_token = next_token(parser);

      block_identifier = name_token.ident;

      if (name_token.type != TOK_IDENT) {
         throw error_struct_create(
                                   "Identifier token expected for a block of statements");
      }
   }

   const p_token_struct& begin_token = next_token(parser);
   if (begin_token.type != TOK_BEGIN) {
      throw error_struct_create("'begin' expected to start a statement block");
   }

   // well then we just have to check the end of the block...
   p_token_struct curr_tok = test_token(parser);
   while ((curr_tok.type != TOK_IDENT) || (curr_tok.ident != block_identifier)) {
      parser_parse_statement(parser);

      curr_tok = test_token(parser);
   }

   curr_tok = next_token(parser);
   if ((curr_tok.type != TOK_IDENT) || (curr_tok.ident != block_identifier)) {
      throw error_struct_create(
                                "Wrong identifier token at the end of a block found");
   }

   curr_tok = next_token(parser);
   if (curr_tok.type != TOK_END) {
      throw error_struct_create(
                                "'end' expected but, '" + curr_tok.ident
                                         + "' found");
   }
}

void parser_parse_assignment_or_block_function(p_parser_struct_ptr parser)
{
   // first we have to check whether it is a block or not...
   p_unsigned_int old_pos = parser->token_index;

   // looking 2 tokens ahead...
   if ((next_token(parser).type == TOK_IDENT) && (next_token(parser).type
            == TOK_BEGIN)) {
      // now we have a block to parse... but the look ahead is skipped...
      parser->token_index = old_pos;

      parser_parse_statement_block(parser, false, "");
      return;
   }

   // resetting the look ahead ...
   parser->token_index = old_pos;

   // resetting the look ahead and testing for function calls...
   if ((next_token(parser).type == TOK_IDENT) && (next_token(parser).type
            == TOK_FUNCTION)) {
      parser->token_index = old_pos;

      // resetting the token look ahead...
      parser_parse_function_definition(parser);
      return;
   }

   // resetting the look ahead ...
   parser->token_index = old_pos;

   // first we have to set the lvalue_expression value...
   // this should tell the parser that an expression must be an assignable value.
   parser->lvalue_expression = true;

   // now we have to parse the lvalue expression... this check is performed by
   // the semantic checking phase... the parser is only checking syntactic correctness.
   parser_parse_value_expression(parser);

   // ok now we have the assignment operator...
   const p_token_struct& curr_tok = next_token(parser);
   if (curr_tok.type != TOK_ASSIGN) {
      throw error_struct_create(
                                "Assignment operator expected, but'"
                                         + curr_tok.ident + "' found");
   }

   // well now we have the final expression... (rvalue now)...
   parser->lvalue_expression = false;

   // the final expression now...
   parser_parse_value_expression(parser);

}

void parser_parse_function_parameter(p_parser_struct_ptr parser)
{
   // syntax: <ident> ':' <type-expression>
   const p_token_struct& name_tok = next_token(parser);
   if (name_tok.type != TOK_IDENT) {
      throw error_struct_create("Identifier expected as a parameter name");
   }

   if (next_token(parser).type != TOK_DPOINT) {
      throw error_struct_create("':' expected after a parameter name");
   }

   // now we have the type expression to parse...
   parser_parse_type_expression(parser, false);
}

void parser_parse_function_parameter_definition(p_parser_struct_ptr parser)
{
   // the first token is just consumed...
   next_token(parser);

   if (test_token(parser).type != TOK_RBRC) {
      parser_parse_function_parameter(parser);

      while (test_token(parser).type == TOK_COLON) {
         // consuming ',' ...
         next_token(parser);
         parser_parse_function_parameter(parser);
      }

      // now we have the ')' token...
      if (next_token(parser).type != TOK_RBRC) {
         throw error_struct_create(
                                   "')' expected at the end of the parameter definition");
      }
   } else {
      // consume the token...
      next_token(parser);
   }
}

/**
 * This function parses the function definition prototype (= forward definition).
 *
 * @param parser This is the parser structure storing important information.
 */
void parser_parse_function_definition_proto(p_parser_struct_ptr parser)
{
   // no token must have been read in advance!

   // the first token is proto
   if (next_token(parser).type != TOK_PROTO) {
      throw error_struct_create(
                                "'proto' expected for a function prototype definition");
   }

   // now the name of the function...
   const p_token_struct& name_tok = next_token(parser);
   if (name_tok.type != TOK_IDENT) {
      throw error_struct_create("Identifier expected as a function name");
   }

   // now the function token...
   if (next_token(parser).type != TOK_FUNCTION) {
      throw error_struct_create("'function' expected after the name");
   }

   // parameter definition, which is optional...
   if (test_token(parser).type == TOK_RBRO) {
      // consuming the current token
      parser_parse_function_parameter_definition(parser);
   }

   // now the return type expression...
   if (next_token(parser).type != TOK_DPOINT) {
      throw error_struct_create("':' expected for a type expression");
   }

   // parsing the type expression... or nil as a special token...
   if (test_token(parser).type == TOK_NIL) {
      // done now...
      next_token(parser);
   } else {
      parser_parse_type_expression(parser, false);
   }
}

/**
 * This function parses the function definition.
 *
 * @param parser The parser structure containing important parser info.
 */
void parser_parse_function_definition(p_parser_struct_ptr parser)
{
   // no token must have been read in advance!

   // first the ident...
   const p_token_struct& name_token = next_token(parser);
   if (name_token.type != TOK_IDENT) {
      throw error_struct_create("Identifier expected as a function name");
   }

   if (next_token(parser).type != TOK_FUNCTION) {
      throw error_struct_create("'function' expected after an identifier");
   }

   // now the optional parameters...
   if (test_token(parser).type == TOK_RBRO) {
      parser_parse_function_parameter_definition(parser);
   }

   // now the return type of the function...
   if (next_token(parser).type != TOK_DPOINT) {
      throw error_struct_create("':' expected for the return type definition");
   }

   if (test_token(parser).type == TOK_NIL) {
      // consuming the token...
      next_token(parser);
   } else {
      parser_parse_type_expression(parser, false);
   }

   parser_parse_statement_block(parser, true, name_token.ident);
}

/**
 * This function parses every statement which is supported by the language.
 *
 * @param parser This is the parser object containing every setting.
 */
void parser_parse_statement(p_parser_struct_ptr parser)
{
   // first we have to parse all preconditions...
   parser_parse_statement_preconditions(parser);

   // testing the first token to check which statement we have to parse...
   const p_token_struct& curr_tok = test_token(parser);
   switch (curr_tok.type)
   {
   case TOK_VAR:
      parser_parse_var_statement(parser);
      break;
   case TOK_PROTO:
      parser_parse_function_definition_proto(parser);
      break;
   default:
      // well the last possible way is the assignment statement...
      // or a block of statements(which is a thread)
      // or a function definition (not a prototype)
      parser_parse_assignment_or_block_function(parser);
      break;
   }

   // and now we have to parse all postconditions...
   parser_parse_statement_postconditions(parser);

   // the last element has to be a ';'
   if (next_token(parser).type != TOK_SEMICOLON) throw error_struct_create(
                                                                           "';' expected at the end of a statement");
}

/**
 * This is the main parser function for programs.
 *
 * @param parser This is the parser object containing all parser relevant settings.
 */
void parser_parse_program(p_parser_struct_ptr parser)
{
   if (next_token(parser).type != TOK_PROGRAM) throw error_struct_create(
                                                                         "'program' expected at the beginning of a file");
   while (test_token(parser).type != TOK_END) {
      parser_parse_statement(parser);
   }

   if (next_token(parser).type != TOK_END) throw error_struct_create(
                                                                     "'end' expected at the end of a file");

   // parsing the entry point
   if (next_token(parser).type != TOK_ADDR) throw error_struct_create(
                                                                      "'@' expected after 'end'");
   if (next_token(parser).type != TOK_IDENT) throw error_struct_create(
                                                                       "Entry function name expected");
   if (next_token(parser).type != TOK_POINT) throw error_struct_create(
                                                                       "'.' expected");
}
